/**
 * 
 */
package org.ajaxanywhere.parser;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * RegexResponseParser.java is
 * 
 * events
 * 
 * @author Alex
 *
 */
@SuppressWarnings("rawtypes")
public class RegexResponseParser implements ResponseParser {
	private final static Logger LOGGER = LoggerFactory.getLogger(RegexResponseParser.class);
	private Pattern imgTag = Pattern.compile("<img\\s", Pattern.CASE_INSENSITIVE);
	private Pattern srcAttr = Pattern.compile("src=('|\")?\\S+('|\")?", Pattern.CASE_INSENSITIVE);
	private Pattern scriptTag = Pattern.compile("<script(\\s|>)", Pattern.CASE_INSENSITIVE);

	@SuppressWarnings("unchecked")
	public HTMLBlock parse(String html) {
		long tstart = System.currentTimeMillis();
		HTMLBlock ret = new HTMLBlock();
		final List scripts = ret.getScriptContents();
		final Set images = ret.getImages();

		// find images to reload
		Matcher matcher = imgTag.matcher(html);
		while (matcher.find()) {
			int start = matcher.start();
			StringBuilder imgHtml = new StringBuilder(256);
			for (int i = start + 5; i < html.length(); i++) {
				if (html.charAt(i) == '>')
					break;
				imgHtml.append(html.charAt(i));
			}
			if (imgHtml.length() == 0)
				continue;

			String src = abstractImageSrcAttribute(imgHtml);
			if (src == null)
				continue;

			images.add(src);
			imgHtml = null;
		}

		// find all scripts
		List<Marker> markers = new ArrayList<Marker>();

		StringMatcher endOfScript = new StringMatcher("</script>");
		StringMatcher startOfCmm = new StringMatcher("<!--");
		StringMatcher endOfCmm = new StringMatcher("-->");

		matcher = scriptTag.matcher(html);
		while (matcher.find()) {
			String g = matcher.group();

			int start = matcher.start() + g.length();
			int markStart = matcher.start();

			if (!g.endsWith(">")) {// not <script>
				for (; start < html.length(); start++) {
					if (html.charAt(start) == '>') {
						start++;
						break;
					}
				}
			}

			StringBuilder scriptContent = new StringBuilder(100);
			boolean notComments = true;
			for (; start < html.length(); start++) {
				char cat = html.charAt(start);
				endOfScript.push(cat);
				startOfCmm.push(cat);
				endOfCmm.push(cat);

				if (startOfCmm.isTarget()) {
					notComments = false;
				} else if (endOfCmm.isTarget()) {
					notComments = true;
				} else if (endOfScript.isTarget()) {
					start++;
					markers.add(new Marker(markStart, start));
					if (scriptContent.length() > endOfScript.length()) {
						scriptContent.delete(scriptContent.length() - endOfScript.length() + 1, scriptContent.length());
					}
					break;
				} else {
					if (notComments) {
						// skip blanks starts
						if (scriptContent.length() == 0 && (cat == ' ' || cat == '\t' || cat == '\r' || cat == '\n')) {

						} else {
							scriptContent.append(cat);
						}
					}
				}
			}
			while (true) {
				if (scriptContent.length() == 0)
					break;
				char cat = scriptContent.charAt(scriptContent.length() - 1);
				if (cat == ' ' || cat == '\t' || cat == '\r' || cat == '\n') {
					scriptContent.deleteCharAt(scriptContent.length() - 1);
				} else
					break;
			}

			if (scriptContent.length() > 0) {
				// remove cdata
				if (scriptContent.indexOf("<![CDATA[") == 0)
					scriptContent.delete(0, 9);
				if (scriptContent.indexOf("]]>") == scriptContent.length() - 3)
					scriptContent.delete(scriptContent.length() - 3, scriptContent.length());
				scripts.add(scriptContent.toString());
			}
			scriptContent = null;
		}
		// remove script in zones
		if (markers.isEmpty())
			ret.setHtmlContent(html);
		else if (markers.size() == 1) {
			StringBuilder htmlSource = new StringBuilder(html.length());

			for (Marker m : markers) {
				for (int i = 0; i < m.start; i++) {
					htmlSource.append(html.charAt(i));
				}
				for (int i = m.end + 1; i < html.length(); i++) {
					htmlSource.append(html.charAt(i));
				}
				break;
			}
			ret.setHtmlContent(htmlSource.toString());
			htmlSource = null;
		} else {
			StringBuilder htmlSource = new StringBuilder(html.length());
			Iterator<Marker> it = markers.iterator();
			Marker p = it.next();
			int start = 0;
			int end = p.start;

			while (it.hasNext()) {
				Marker c = it.next();
				for (int i = start; i < end; i++) {
					htmlSource.append(html.charAt(i));
				}
				start = p.end + 1;
				end = c.start;
				for (int i = start; i < end; i++) {
					htmlSource.append(html.charAt(i));
				}

				p = c;
			}
			ret.setHtmlContent(htmlSource.toString());
			htmlSource = null;
		}
		long tend = System.currentTimeMillis();
		LOGGER.debug("Time costs for parsing html: " + (tend - tstart));
		return ret;
	}

	/**
	 * @param imgHtml
	 * @return
	 */
	private String abstractImageSrcAttribute(StringBuilder imgHtml) {
		Matcher m = srcAttr.matcher(imgHtml);
		if (m.find()) {
			String src = m.group().substring(4);
			if (src.length() == 0)
				return null;

			if (src.charAt(0) == '\'')
				src = src.substring(1);
			if (src.charAt(0) == '"')
				src = src.substring(1);

			if (src.charAt(src.length() - 1) == '\'')
				src = src.substring(0, src.length() - 1);
			if (src.charAt(src.length() - 1) == '"')
				src = src.substring(0, src.length() - 1);

			return src;
		}
		return null;
	}

	private class Marker {
		int start;
		int end;

		public Marker(int start, int end) {
			super();
			this.start = start;
			this.end = end;
		}
	}
}
